package cn.test.util;

import org.apache.commons.collections4.CollectionUtils;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

public class DateUtil {

    private DateUtil() {
    }

    public static final String PRECEDING_INDEX = "P";

    public static final String FOLLOWING_INDEX = "F";


    public static final DateTimeFormatter DEFAULT_FORMAT_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static LocalDate toLocalDate(Object date1) {
        if (date1 instanceof LocalDate) {
            return (LocalDate) date1;
        } else if (date1 instanceof Date) {
            Date date3 = (Date) date1;
            return date3.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        } else if (date1 instanceof String) {
            return LocalDate.parse((String) date1);
        }
        return null;
    }

    public static LocalDate precedingAdjust(LocalDate unAdjDate, List<LocalDate> holidays, List<LocalDate> businessDays) {
        LocalDate bus = unAdjDate;
        while (isNotWorkDay(bus, holidays, businessDays)) {
            bus = bus.minusDays(1);
        }
        return bus;
    }

    public static LocalDate followingAdjust(LocalDate unAdjDate, List<LocalDate> holidays, List<LocalDate> businessDays) {
        LocalDate bus = unAdjDate;
        while (isNotWorkDay(bus, holidays, businessDays)) {
            bus = bus.plusDays(1);
        }
        return bus;
    }

    public static LocalDate precedingAdjust(LocalDate unAdjDate, List<LocalDate> holidays, List<LocalDate> businessDays, int adjustDays) {
        LocalDate bus = precedingAdjust(unAdjDate, holidays, businessDays);
        if (adjustDays == BigDecimal.ZERO.intValue()) return bus;

        int daysIndex = Math.abs(adjustDays);
        while (daysIndex > 0) {
            bus = bus.minusDays(1);
            bus = precedingAdjust(bus, holidays, businessDays);
            daysIndex--;
        }
        return bus;
    }

    public static LocalDate followingAdjust(LocalDate unAdjDate, List<LocalDate> holidays, List<LocalDate> businessDays, int adjustDays) {
        LocalDate bus = followingAdjust(unAdjDate, holidays, businessDays);
        if (adjustDays == BigDecimal.ZERO.intValue()) return bus;

        int daysIndex = Math.abs(adjustDays);
        while (daysIndex > 0) {
            bus = bus.plusDays(1);
            bus = followingAdjust(bus, holidays, businessDays);
            daysIndex--;
        }
        return bus;
    }

    public static boolean isNotWorkDay(LocalDate target, List<LocalDate> holidays, List<LocalDate> businessDays) {
        if (isHoliday(target, holidays)) {
            return Boolean.TRUE;
        }
        if (isWeekEnd(target) && (CollectionUtils.isEmpty(businessDays) || !businessDays.contains(target))) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    private static boolean isWeekEnd(LocalDate day) {
        return day.getDayOfWeek() == DayOfWeek.SATURDAY || day.getDayOfWeek() == DayOfWeek.SUNDAY;
    }

    private static boolean isHoliday(LocalDate day, List<LocalDate> holidays) {
        return CollectionUtils.isNotEmpty(holidays) && holidays.contains(day);
    }

    public static int currentQuarter() {
        int month = LocalDate.now().getMonthValue();
        return (month % 3 == 0 ? month / 3 : month / 3 + 1);
    }

}
